<!DOCTYPE html>
<html lang=en>
<head>
    <!-- so meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="Subject：可以理解为与shiro打交道的对象，该对象封装了一些对方的信息，shiro可以通过subject拿到这些信息 SecurityManager：Shiro的总经理，通过指使Authorizer和Authenticator等对subject进行授权和身份验证等工作 Realm：管理着一些如用户、角色、权限等重要信息，Shiro中所需的这些重要信息都是从Realm这里获取的，Realm">
<meta name="keywords" content="Java,SpringBoot,Shiro">
<meta property="og:type" content="article">
<meta property="og:title" content="安全权限框架之-springboot整合shiro">
<meta property="og:url" content="http:&#x2F;&#x2F;yoursite.com&#x2F;2019&#x2F;10&#x2F;31&#x2F;%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro&#x2F;index.html">
<meta property="og:site_name" content="ECIN&#39;S BLOG">
<meta property="og:description" content="Subject：可以理解为与shiro打交道的对象，该对象封装了一些对方的信息，shiro可以通过subject拿到这些信息 SecurityManager：Shiro的总经理，通过指使Authorizer和Authenticator等对subject进行授权和身份验证等工作 Realm：管理着一些如用户、角色、权限等重要信息，Shiro中所需的这些重要信息都是从Realm这里获取的，Realm">
<meta property="og:locale" content="en">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571404808823.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571623692995.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571623564852.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571623796384.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571623838500.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571672890938.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571804377475.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571804400820.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571804418056.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571804434170.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573195336847.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573195656698.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573195510558.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573195603643.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573613696767.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573619171317.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1573619237325.jpg">
<meta property="og:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571762848189.jpg">
<meta property="og:updated_time" content="2020-01-30T02:22:55.787Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http:&#x2F;&#x2F;106.15.200.82&#x2F;source&#x2F;1571404808823.jpg">
    
    
        
          
              <link rel="shortcut icon" href="/images/favicon.ico">
          
        
        
          
            <link rel="icon" type="image/png" href="/images/favicon-192x192.png" sizes="192x192">
          
        
        
          
            <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png">
          
        
    
    <!-- title -->
    <title>安全权限框架之-springboot整合shiro</title>
    <!-- styles -->
    <link rel="stylesheet" href="/css/style.css">
    <!-- persian styles -->
    
      <link rel="stylesheet" href="/css/rtl.css">
    
    <!-- rss -->
    
    
</head>

<body class="max-width mx-auto px3 ltr">
    
      <div id="header-post">
  <a id="menu-icon" href="#"><i class="fas fa-bars fa-lg"></i></a>
  <a id="menu-icon-tablet" href="#"><i class="fas fa-bars fa-lg"></i></a>
  <a id="top-icon-tablet" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;"><i class="fas fa-chevron-up fa-lg"></i></a>
  <span id="menu">
    <span id="nav">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Writing</a></li>
         
          <li><a href="/projects_url">Projects</a></li>
        
      </ul>
    </span>
    <br/>
    <span id="actions">
      <ul>
        
        <li><a class="icon" href="/2019/10/31/Linux%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8%E9%98%BF%E9%87%8C%E4%BA%91%E9%95%9C%E5%83%8F/"><i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i></a></li>
        
        
        <li><a class="icon" href="/2019/10/30/%E6%95%B4%E7%90%86%E4%BA%862019%E5%B9%B4Java%E9%9D%A2%E8%AF%95%E9%A2%98%EF%BC%88%E9%9B%86%20%E5%90%88+%E5%B9%B6%E5%8F%91+%E8%B0%83%E4%BC%98+%E5%BE%AE%E6%9C%8D%E5%8A%A1+spring+Redis%EF%BC%89%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89/"><i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i></a></li>
        
        <li><a class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i></a></li>
        <li><a class="icon" href="#"><i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i></a></li>
      </ul>
      <span id="i-prev" class="info" style="display:none;">Previous post</span>
      <span id="i-next" class="info" style="display:none;">Next post</span>
      <span id="i-top" class="info" style="display:none;">Back to top</span>
      <span id="i-share" class="info" style="display:none;">Share post</span>
    </span>
    <br/>
    <div id="share" style="display: none">
      <ul>
  <li><a class="icon" href="http://www.facebook.com/sharer.php?u=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/" target="_blank" rel="noopener"><i class="fab fa-facebook " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://twitter.com/share?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&text=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-twitter " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.linkedin.com/shareArticle?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-linkedin " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&is_video=false&description=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-pinterest " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=安全权限框架之-springboot整合shiro&body=Check out this article: http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/" target="_blank" rel="noopener"><i class="fas fa-envelope " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://getpocket.com/save?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-get-pocket " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://reddit.com/submit?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-reddit " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.stumbleupon.com/submit?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-stumbleupon " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://digg.com/submit?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-digg " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.tumblr.com/share/link?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&name=安全权限框架之-springboot整合shiro&description=" target="_blank" rel="noopener"><i class="fab fa-tumblr " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://news.ycombinator.com/submitlink?u=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&t=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-hacker-news " aria-hidden="true"></i></a></li>
</ul>

    </div>
    <div id="toc">
      <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#接下来我们开始简单的整合"><span class="toc-number">1.</span> <span class="toc-text">接下来我们开始简单的整合</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#pom-xml添加"><span class="toc-number">1.1.</span> <span class="toc-text">pom.xml添加</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#第一步就是手写一个配置类，命名为ShiroConfig"><span class="toc-number">1.2.</span> <span class="toc-text">第一步就是手写一个配置类，命名为ShiroConfig</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接下来手写Realm类"><span class="toc-number">1.3.</span> <span class="toc-text">接下来手写Realm类</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Controller，在Controller中写刚刚的几个url"><span class="toc-number">1.4.</span> <span class="toc-text">Controller，在Controller中写刚刚的几个url</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接下来使用postman进行接口测试"><span class="toc-number">1.5.</span> <span class="toc-text">接下来使用postman进行接口测试</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#输入不需要认证的url-“-list”进行测试"><span class="toc-number">1.5.1.</span> <span class="toc-text">输入不需要认证的url “/list”进行测试</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#输入需要认证的url进行测试"><span class="toc-number">1.5.2.</span> <span class="toc-text">输入需要认证的url进行测试</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#现在开始认证"><span class="toc-number">1.5.3.</span> <span class="toc-text">现在开始认证</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#再次输入需要认证的url-“-info”"><span class="toc-number">1.5.4.</span> <span class="toc-text">再次输入需要认证的url “/info”</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#角色和权限的关系"><span class="toc-number">1.6.</span> <span class="toc-text">角色和权限的关系</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#首先是数据库设计，先给出一个简单的数据表"><span class="toc-number">1.7.</span> <span class="toc-text">首先是数据库设计，先给出一个简单的数据表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）"><span class="toc-number">1.8.</span> <span class="toc-text">接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#DAO数据层使用简单的-Mapper注解"><span class="toc-number">1.8.1.</span> <span class="toc-text">DAO数据层使用简单的@Mapper注解</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#业务层接口"><span class="toc-number">1.8.2.</span> <span class="toc-text">业务层接口</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#注入service业务层接口实现"><span class="toc-number">1.8.3.</span> <span class="toc-text">注入service业务层接口实现</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#现在认证可以使用数据库了"><span class="toc-number">1.8.4.</span> <span class="toc-text">现在认证可以使用数据库了</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#授权"><span class="toc-number">1.8.5.</span> <span class="toc-text">授权</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#有两种方法，一种是在控制层添加注解"><span class="toc-number">1.9.</span> <span class="toc-text">有两种方法，一种是在控制层添加注解</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过"><span class="toc-number">1.10.</span> <span class="toc-text">另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#设置没有权限返回的接口"><span class="toc-number">1.10.1.</span> <span class="toc-text">设置没有权限返回的接口</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Shiro-md5加密，加盐"><span class="toc-number">1.11.</span> <span class="toc-text">Shiro md5加密，加盐</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#在ShiroConfig类中加入Bean"><span class="toc-number">1.11.0.1.</span> <span class="toc-text">在ShiroConfig类中加入Bean</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#修改ShiroConfig类中的Bean-Realm"><span class="toc-number">1.11.0.2.</span> <span class="toc-text">修改ShiroConfig类中的Bean-Realm</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#添加一个容器类Encryption"><span class="toc-number">1.11.0.3.</span> <span class="toc-text">添加一个容器类Encryption</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#最后修改下Realm的认证逻辑"><span class="toc-number">1.11.0.4.</span> <span class="toc-text">最后修改下Realm的认证逻辑</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#设置rememberMe状态保留"><span class="toc-number">1.12.</span> <span class="toc-text">设置rememberMe状态保留</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#添加两个Bean"><span class="toc-number">1.12.0.1.</span> <span class="toc-text">添加两个Bean</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#然后添加到SecurityManager中注入上面两个Bean"><span class="toc-number">1.12.0.2.</span> <span class="toc-text">然后添加到SecurityManager中注入上面两个Bean</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#在ShiroFilterFactoryBean中设置接口（页面）"><span class="toc-number">1.12.0.3.</span> <span class="toc-text">在ShiroFilterFactoryBean中设置接口（页面）</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#最后在-login-接口中"><span class="toc-number">1.12.0.4.</span> <span class="toc-text">最后在 /login 接口中</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#测试"><span class="toc-number">1.12.0.5.</span> <span class="toc-text">测试</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Shiro整合session-redis实现分布式共享session"><span class="toc-number">1.13.</span> <span class="toc-text">Shiro整合session+redis实现分布式共享session</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#第一步添加依赖"><span class="toc-number">1.13.0.1.</span> <span class="toc-text">第一步添加依赖</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#shiroConfig配置中"><span class="toc-number">1.13.0.2.</span> <span class="toc-text">shiroConfig配置中</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#添加到securityManager中"><span class="toc-number">1.13.0.3.</span> <span class="toc-text">添加到securityManager中</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#测试-1"><span class="toc-number">1.13.0.4.</span> <span class="toc-text">测试</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Shiro登陆失败次数限制"><span class="toc-number">1.14.</span> <span class="toc-text">Shiro登陆失败次数限制</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#数据库表设计"><span class="toc-number">2.</span> <span class="toc-text">数据库表设计</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#用户表"><span class="toc-number">2.1.</span> <span class="toc-text">用户表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#角色表"><span class="toc-number">2.2.</span> <span class="toc-text">角色表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#权限表"><span class="toc-number">2.3.</span> <span class="toc-text">权限表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#用户角色关系表"><span class="toc-number">2.4.</span> <span class="toc-text">用户角色关系表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#角色权限关系表"><span class="toc-number">2.5.</span> <span class="toc-text">角色权限关系表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#session会话管理"><span class="toc-number">2.6.</span> <span class="toc-text">session会话管理</span></a></li></ol></li></ol>
    </div>
  </span>
</div>

    
    <div class="content index py4">
        
        <article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    
    <h1 class="posttitle" itemprop="name headline">
        安全权限框架之-springboot整合shiro
    </h1>



    <div class="meta">
      <span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
        <span itemprop="name">ECIN'S BLOG</span>
      </span>
      
    <div class="postdate">
      
        <time datetime="2019-10-31T09:20:58.000Z" itemprop="datePublished">2019-10-31</time>
        
      
    </div>


      

      
    <div class="article-tag">
        <i class="fas fa-tag"></i>
        <a class="tag-link" href="/tags/Java/" rel="tag">Java</a>, <a class="tag-link" href="/tags/Shiro/" rel="tag">Shiro</a>, <a class="tag-link" href="/tags/SpringBoot/" rel="tag">SpringBoot</a>
    </div>


    </div>
  </header>
  

  <div class="content" itemprop="articleBody">
    <p><img src="http://106.15.200.82/source/1571404808823.jpg" alt="image.png"></p>
<p><strong>Subject</strong>：可以理解为与shiro打交道的对象，该对象封装了一些对方的信息，shiro可以通过subject拿到这些信息</p>
<p><strong>SecurityManage</strong>r：Shiro的总经理，通过指使Authorizer和Authenticator等对subject进行授权和身份验证等工作</p>
<p><strong>Realm</strong>：管理着一些如用户、角色、权限等重要信息，Shiro中所需的这些重要信息都是从Realm这里获取的，Realm本质上就是一个重要信息的数据源</p>
<p><strong>Authenticator</strong>：认证器，负责Subject的认证操作，认证过程就是根据Subject提供的信息通过Realm查询到相关信息，然后做对比，支持扩展</p>
<p><strong>Authorizer</strong>：授权器，控制着Subject对服务资源的访问权限</p>
<p><strong>SessionManager</strong>：用于管理Session，这个Session可以是web的也可以不是web的。</p>
<p><strong>SessionDao</strong>：把Session的 CRUD和存储介质联系起来的工具，存储介质可以是数据库，也可以是缓存，比如把session放到redis里面</p>
<p><strong>CacheManager</strong>：缓存控制器，Realm管理的数据（用户、角色、权限）可以放到缓存里由CacheManager管理，提高认证授权等的速度</p>
<p><strong>Cryptography</strong>：加密组件，Shiro提供了很多加解密算法的组件</p>
<h3 id="接下来我们开始简单的整合"><a href="#接下来我们开始简单的整合" class="headerlink" title="接下来我们开始简单的整合"></a>接下来我们开始简单的整合</h3><h4 id="pom-xml添加"><a href="#pom-xml添加" class="headerlink" title="pom.xml添加"></a>pom.xml添加</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.apache.shiro&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;shiro-spring&lt;/artifactId&gt;</span><br><span class="line">    &lt;version&gt;1.3.2&lt;/version&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure>

<h4 id="第一步就是手写一个配置类，命名为ShiroConfig"><a href="#第一步就是手写一个配置类，命名为ShiroConfig" class="headerlink" title="第一步就是手写一个配置类，命名为ShiroConfig"></a>第一步就是手写一个配置类，命名为ShiroConfig</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.shiro.spring.web.ShiroFilterFactoryBean;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.web.mgt.DefaultWebSecurityManager;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.LinkedHashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"shiroFilter"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> ShiroFilterFactoryBean <span class="title">shiroFilter</span><span class="params">()</span></span>&#123;</span><br><span class="line"></span><br><span class="line">        ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> ShiroFilterFactoryBean();</span><br><span class="line">        shiroFilterFactoryBean.setSecurityManager(securityManager());</span><br><span class="line">        shiroFilterFactoryBean.setLoginUrl(<span class="string">"/none"</span>);</span><br><span class="line">        shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">"/none"</span>);</span><br><span class="line">        Map&lt;String,String&gt; map = <span class="keyword">new</span> LinkedHashMap&lt;String,String&gt;();</span><br><span class="line">        <span class="comment">/*anon表示可以匿名访问，authc表示必须认证通过才能访问*/</span></span><br><span class="line">        map.put(<span class="string">"/list"</span>,<span class="string">"anon"</span>);</span><br><span class="line">        map.put(<span class="string">"/login"</span>,<span class="string">"anon"</span>);</span><br><span class="line">        map.put(<span class="string">"/info"</span>,<span class="string">"authc"</span>);</span><br><span class="line">        <span class="comment">/*这行代码需要放在所有权限设置的最后，不然会导致所有的url被拦截，剩余的都需要认证*/</span></span><br><span class="line">        map.put(<span class="string">"/**"</span>,<span class="string">"authc"</span>);</span><br><span class="line">        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> shiroFilterFactoryBean;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"securityManager"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> DefaultWebSecurityManager <span class="title">securityManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        DefaultWebSecurityManager defaultWebSecurityManager = <span class="keyword">new</span> DefaultWebSecurityManager();</span><br><span class="line">        defaultWebSecurityManager.setRealm(realm());</span><br><span class="line">        <span class="keyword">return</span> defaultWebSecurityManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span>(name = <span class="string">"realm"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Realm <span class="title">realm</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Realm();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里有三个Bean，需要自己操作的就是shiroFilter和realm，shiroFilter设置过滤器，realm是一个继承AuthorizingRealm的类，需要实现两个方法doGetAuthorizationInfo和doGetAuthenticationInfo执行授权与认证。除此之外，设置了四个url  /none /list /login /info,<br>/none为未登录与认证跳转的url，/list和/login为不用认证的url，/info为需要认证的url。</p>
<h4 id="接下来手写Realm类"><a href="#接下来手写Realm类" class="headerlink" title="接下来手写Realm类"></a>接下来手写Realm类</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">import org.apache.shiro.SecurityUtils;</span><br><span class="line">import org.apache.shiro.authc.*;</span><br><span class="line">import org.apache.shiro.authz.AuthorizationInfo;</span><br><span class="line">import org.apache.shiro.authz.SimpleAuthorizationInfo;</span><br><span class="line">import org.apache.shiro.realm.AuthorizingRealm;</span><br><span class="line">import org.apache.shiro.subject.PrincipalCollection;</span><br><span class="line"></span><br><span class="line">import java.util.HashMap;</span><br><span class="line">import java.util.HashSet;</span><br><span class="line">import java.util.Set;</span><br><span class="line"></span><br><span class="line">public class Realm extends AuthorizingRealm &#123;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) &#123;</span><br><span class="line">        System.out.println(&quot;执行授权逻辑&quot;);</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /*身份认证*/</span><br><span class="line">    @Override</span><br><span class="line">    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException &#123;</span><br><span class="line">        System.out.println(&quot;执行认证逻辑&quot;);</span><br><span class="line">        /*这里账户密码模拟数据库*/</span><br><span class="line">        String username = &quot;qwer&quot;;</span><br><span class="line">        String password = &quot;1234&quot;;</span><br><span class="line">        /*获取用户认证传来的封装的账户密码令牌*/</span><br><span class="line">        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;</span><br><span class="line">        /*返回用户名不存在，底层抛出UnKnowAccountException异常*/</span><br><span class="line">        if(!token.getUsername().equals(username))&#123;</span><br><span class="line">            return null;</span><br><span class="line">        &#125;</span><br><span class="line">        return new SimpleAuthenticationInfo(&quot;&quot;,password,&quot;&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="Controller，在Controller中写刚刚的几个url"><a href="#Controller，在Controller中写刚刚的几个url" class="headerlink" title="Controller，在Controller中写刚刚的几个url"></a>Controller，在Controller中写刚刚的几个url</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line">import org.apache.shiro.SecurityUtils;</span><br><span class="line">import org.apache.shiro.authc.*;</span><br><span class="line">import org.apache.shiro.subject.Subject;</span><br><span class="line">import org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line">import org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line">import org.springframework.data.redis.core.StringRedisTemplate;</span><br><span class="line">import org.springframework.web.bind.annotation.*;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.HashMap;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line">@RestController</span><br><span class="line">public class test &#123;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(&quot;/list&quot;)</span><br><span class="line">    public List&lt;HashMap&lt;String,Integer&gt;&gt; Map()&#123;</span><br><span class="line">        List&lt;HashMap&lt;String,Integer&gt;&gt; list = new ArrayList&lt;&gt;();</span><br><span class="line">        HashMap&lt;String,Integer&gt; map = new HashMap&lt;&gt;();</span><br><span class="line">        map.put(&quot;One&quot;,1);</span><br><span class="line">        map.put(&quot;Two&quot;,2);</span><br><span class="line">        map.put(&quot;Three&quot;,3);</span><br><span class="line">        HashMap&lt;String,Integer&gt; map0 = new HashMap&lt;&gt;();</span><br><span class="line">        map0.put(&quot;One&quot;,1);</span><br><span class="line">        map0.put(&quot;Two&quot;,2);</span><br><span class="line">        map0.put(&quot;Three&quot;,3);</span><br><span class="line">        list.add(map);</span><br><span class="line">        list.add(map0);</span><br><span class="line">        return list;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(&quot;/none&quot;)</span><br><span class="line">    public String none()&#123;</span><br><span class="line">        return &quot;You can&apos;t get the service&quot;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(&quot;/info&quot;)</span><br><span class="line">    public String info()&#123;</span><br><span class="line">        return &quot;This is info required login&quot;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @RequestMapping(&quot;/login&quot;)</span><br><span class="line">    public String Login(@RequestParam(&quot;username&quot;) String username,@RequestParam(&quot;password&quot;)String password)&#123;</span><br><span class="line">        /*创建一个subject*/</span><br><span class="line">        Subject subject = SecurityUtils.getSubject();</span><br><span class="line">        /*封装用户数据*/</span><br><span class="line">        UsernamePasswordToken token = new UsernamePasswordToken(username,password);</span><br><span class="line">        try &#123;</span><br><span class="line">            /*执行登录,然后这里会跳转到Realm的认证逻辑中*/</span><br><span class="line">            subject.login(token);</span><br><span class="line">            return &quot;登陆成功&quot;;</span><br><span class="line">        &#125; catch (UnknownAccountException uae) &#123;</span><br><span class="line">            return &quot;未知账户&quot;;</span><br><span class="line">        &#125; catch (IncorrectCredentialsException ice) &#123;</span><br><span class="line">            return &quot;密码不正确&quot;;</span><br><span class="line">        &#125; catch (LockedAccountException lae) &#123;</span><br><span class="line">            return &quot;账户已锁定&quot;;</span><br><span class="line">        &#125; catch (ExcessiveAttemptsException eae) &#123;</span><br><span class="line">            return &quot;用户名或密码错误次数过多&quot;;</span><br><span class="line">        &#125; catch (AuthenticationException ae) &#123;</span><br><span class="line">            return &quot;用户名或密码不正确！&quot;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="接下来使用postman进行接口测试"><a href="#接下来使用postman进行接口测试" class="headerlink" title="接下来使用postman进行接口测试"></a>接下来使用postman进行接口测试</h4><h5 id="输入不需要认证的url-“-list”进行测试"><a href="#输入不需要认证的url-“-list”进行测试" class="headerlink" title="输入不需要认证的url “/list”进行测试"></a>输入不需要认证的url “/list”进行测试</h5><p><img src="http://106.15.200.82/source/1571623692995.jpg" alt="image.png"><br>顺利获取接口提供数据</p>
<h5 id="输入需要认证的url进行测试"><a href="#输入需要认证的url进行测试" class="headerlink" title="输入需要认证的url进行测试"></a>输入需要认证的url进行测试</h5><p><img src="http://106.15.200.82/source/1571623564852.jpg" alt="image.png"><br>发现跳转到了/none页面，这是因为未执行认证。</p>
<h5 id="现在开始认证"><a href="#现在开始认证" class="headerlink" title="现在开始认证"></a>现在开始认证</h5><p><img src="http://106.15.200.82/source/1571623796384.jpg" alt="image.png"><br>认证成功</p>
<h5 id="再次输入需要认证的url-“-info”"><a href="#再次输入需要认证的url-“-info”" class="headerlink" title="再次输入需要认证的url “/info”"></a>再次输入需要认证的url “/info”</h5><p><img src="http://106.15.200.82/source/1571623838500.jpg" alt="image.png"><br>成功获取接口数据</p>
<h4 id="角色和权限的关系"><a href="#角色和权限的关系" class="headerlink" title="角色和权限的关系"></a>角色和权限的关系</h4><p><img src="http://106.15.200.82/source/1571672890938.jpg" alt="image.png"></p>
<p>那么最基本的使用已经说完了，现在开始实战部分</p>
<h4 id="首先是数据库设计，先给出一个简单的数据表"><a href="#首先是数据库设计，先给出一个简单的数据表" class="headerlink" title="首先是数据库设计，先给出一个简单的数据表"></a>首先是数据库设计，先给出一个简单的数据表</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">DROP TABLE IF EXISTS `user_role` ;</span><br><span class="line">DROP TABLE IF EXISTS `role_permission` ;</span><br><span class="line">DROP TABLE IF EXISTS `user` ;</span><br><span class="line">DROP TABLE IF EXISTS `role` ;</span><br><span class="line">DROP TABLE IF EXISTS `permission` ;</span><br><span class="line"></span><br><span class="line"># 用户表</span><br><span class="line">CREATE TABLE `user` (`id` BIGINT PRIMARY KEY NOT NULL ,`username` VARCHAR (20),`password` VARCHAR(300));</span><br><span class="line"># 角色表</span><br><span class="line">CREATE TABLE `role` (`id` BIGINT PRIMARY KEY NOT NULL ,`description` VARCHAR(3600),`role` VARCHAR(3600));</span><br><span class="line"># 权限表</span><br><span class="line">CREATE TABLE `permission` (`id` BIGINT PRIMARY KEY NOT NULL ,`name` VARCHAR(200),`description` VARCHAR(3600));</span><br><span class="line"># 用户角色表</span><br><span class="line">CREATE TABLE `user_role` (`user_id` BIGINT, `role_id` BIGINT,</span><br><span class="line">    FOREIGN KEY (`user_id`) REFERENCES `user`(`id`),</span><br><span class="line">        FOREIGN KEY (`role_id`) REFERENCES `role`(`id`));</span><br><span class="line"># 角色权限表</span><br><span class="line">CREATE TABLE `role_permission` (`role_id` BIGINT, `permission_id` BIGINT,</span><br><span class="line">                          FOREIGN KEY (`role_id`) REFERENCES `role`(`id`),</span><br><span class="line">                          FOREIGN KEY (`permission_id`) REFERENCES `permission`(`id`));</span><br><span class="line"></span><br><span class="line"># 一个用户可以有多个角色，每个角色有不同的权限，可以有多个权限</span><br><span class="line">INSERT `user` (id, username, password) VALUES (10000,&apos;normal&apos;,&apos;1234&apos;);</span><br><span class="line">INSERT `user` (id, username, password) VALUES (10001,&apos;vip1&apos;,&apos;1234&apos;);</span><br><span class="line">INSERT `user` (id, username, password) VALUES (10002,&apos;vip2&apos;,&apos;1234&apos;);</span><br><span class="line"></span><br><span class="line">INSERT `role` (id, description, role) VALUES (10000,&apos;没啥权限，底层贫穷用户。&apos;,&apos;普通用户&apos;);</span><br><span class="line">INSERT `role` (id, description, role) VALUES (10001,&apos;vip黄金会员，拥有观看独播视频和更换皮肤的功能。&apos;,&apos;vip黄金会员&apos;);</span><br><span class="line">INSERT `role` (id, description, role) VALUES (10002,&apos;vip白金会员，拥有vip黄金会员的所有权限，还有不限速功能&apos;,&apos;vip白金会员&apos;);</span><br><span class="line"></span><br><span class="line"># INSERT `permission` (id, name, description) VALUES (10000,&apos;normal&apos;,&apos;观看普通视频&apos;);</span><br><span class="line">INSERT `permission` (id, name, description) VALUES (10000,&apos;vip1&apos;,&apos;观看独播视频&apos;);</span><br><span class="line">INSERT `permission` (id, name, description) VALUES (10001,&apos;vip1&apos;,&apos;更换高级皮肤&apos;);</span><br><span class="line">INSERT `permission` (id, name, description) VALUES (10002,&apos;vip2&apos;,&apos;缓存不限速&apos;);</span><br><span class="line"></span><br><span class="line"># 添加角色权限</span><br><span class="line">INSERT `role_permission` (role_id, permission_id) VALUES (10001, 10000);</span><br><span class="line">INSERT `role_permission` (role_id, permission_id) VALUES (10001, 10001);</span><br><span class="line"></span><br><span class="line">INSERT `role_permission` (role_id, permission_id) VALUES (10002, 10000);</span><br><span class="line">INSERT `role_permission` (role_id, permission_id) VALUES (10002, 10001);</span><br><span class="line">INSERT `role_permission` (role_id, permission_id) VALUES (10002, 10002);</span><br><span class="line"></span><br><span class="line"># 添加用户角色</span><br><span class="line">INSERT `user_role` (user_id, role_id) VALUES (10000, 10000);</span><br><span class="line">INSERT `user_role` (user_id, role_id) VALUES (10001, 10001);</span><br><span class="line">INSERT `user_role` (user_id, role_id) VALUES (10002, 10002);</span><br><span class="line"></span><br><span class="line"># 获取角色id</span><br><span class="line">SELECT `user_role`.`role_id` FROM `user_role` WHERE `user_id` = 10001;</span><br><span class="line"># 获取该角色的权限</span><br><span class="line">SELECT `role_permission`.`permission_id` FROM `role_permission` WHERE `role_id` IN(SELECT `user_role`.`role_id` FROM `user_role` WHERE `user_id` = 10000);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">SELECT `role`.`role` FROM `role` WHERE `role`.`id` IN (SELECT `user_role`.`role_id` FROM `user_role` WHERE `user_id` = 10002);</span><br><span class="line"></span><br><span class="line">SELECT `permission`.* FROM `permission` WHERE `permission`.`id` IN</span><br><span class="line">    (SELECT `role_permission`.`permission_id` FROM `role_permission` WHERE `role_id` IN</span><br><span class="line">        (SELECT `role`.`id` FROM `role` WHERE `role`.`id` IN</span><br><span class="line">            (SELECT `user_role`.`role_id` FROM `user_role` WHERE `user_id` = 10001)));</span><br><span class="line"></span><br><span class="line">SELECT * FROM user;</span><br></pre></td></tr></table></figure>
<p>这就是一个简单的数据库设计，需要五张表</p>
<blockquote>
<p>user -用户表</p>
</blockquote>
<blockquote>
<p>role -角色表</p>
</blockquote>
<blockquote>
<p>permission -权限表</p>
</blockquote>
<blockquote>
<p>user_role -用户角色表</p>
</blockquote>
<blockquote>
<p>role_permission -角色权限表</p>
</blockquote>
<p>用户表就是普通的用户表</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public class User implements Serializable &#123;</span><br><span class="line"></span><br><span class="line">    private Integer id;</span><br><span class="line">    private String username;</span><br><span class="line">    private String password;</span><br><span class="line"></span><br><span class="line">    /*get and set*/</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>角色表</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public class Role &#123;</span><br><span class="line">    private int id;</span><br><span class="line">    private String description;</span><br><span class="line">    private String role;</span><br><span class="line"></span><br><span class="line">    /*get and set*/</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>权限表</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">public class Permission &#123;</span><br><span class="line">    private Integer id;</span><br><span class="line">    private String name;</span><br><span class="line">    private String description;</span><br><span class="line">    /*get and set*/</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>其他两个表不用创建实体类，这里的用户可以对应多个角色，每个角色可以对应多个权限，比如腾讯QQ的普通用户和vip用户，普通用户无法享受更换高级装饰的功能，但是vip用户可以。在这里，普通用户和vip用户是角色，更换高级装饰功能是权限，是vip特有的权限，所以数据表user_role与role_permission要对应这种关系。</p>
<h4 id="接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）"><a href="#接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）" class="headerlink" title="接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）"></a>接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）</h4><h5 id="DAO数据层使用简单的-Mapper注解"><a href="#DAO数据层使用简单的-Mapper注解" class="headerlink" title="DAO数据层使用简单的@Mapper注解"></a>DAO数据层使用简单的@Mapper注解</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">@Select(&quot;SELECT `permission`.* FROM `permission` WHERE `permission`.`id` IN\n&quot; +</span><br><span class="line">        &quot;    (SELECT `role_permission`.`permission_id` FROM `role_permission` WHERE `role_id` IN\n&quot; +</span><br><span class="line">        &quot;        (SELECT `role`.`id` FROM `role` WHERE `role`.`id` IN\n&quot; +</span><br><span class="line">        &quot;            (SELECT `user_role`.`role_id` FROM `user_role` WHERE `user_id` = #&#123;id&#125;)));&quot;)</span><br><span class="line">List&lt;Permission&gt; getPermissionByUserId(Integer id);</span><br><span class="line"></span><br><span class="line">@Select(&quot;SELECT `role`.* FROM `role` WHERE `role`.`id` IN (SELECT `user_role`.`role_id` FROM `user_role` WHERE `user_id` = #&#123;id&#125;);&quot;)</span><br><span class="line">List&lt;Role&gt; getRoleByUserId(Integer id);</span><br><span class="line"></span><br><span class="line">@Select(&quot;SELECT `user`.* FROM `user` WHERE `user`.`username` = #&#123;username&#125;;&quot;)</span><br><span class="line">User getUserByUsername(String username);</span><br></pre></td></tr></table></figure>
<h5 id="业务层接口"><a href="#业务层接口" class="headerlink" title="业务层接口"></a>业务层接口</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Permission&gt; getPermissionByUserId(Integer id);</span><br><span class="line">List&lt;Role&gt; getRoleByUserId(Integer id);</span><br><span class="line">User getUserByUsername(String username);</span><br></pre></td></tr></table></figure>
<h5 id="注入service业务层接口实现"><a href="#注入service业务层接口实现" class="headerlink" title="注入service业务层接口实现"></a>注入service业务层接口实现</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public List&lt;Permission&gt; getPermissionByUserId(Integer id) &#123;</span><br><span class="line">    return userDao.getPermissionByUserId(id);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override</span><br><span class="line">public List&lt;Role&gt; getRoleByUserId(Integer id) &#123;</span><br><span class="line">    return userDao.getRoleByUserId(id);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override</span><br><span class="line">public User getUserByUsername(String username) &#123;</span><br><span class="line">    return userDao.getUserByUsername(username);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="现在认证可以使用数据库了"><a href="#现在认证可以使用数据库了" class="headerlink" title="现在认证可以使用数据库了"></a>现在认证可以使用数据库了</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">/*身份认证*/</span><br><span class="line">@Override</span><br><span class="line">protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException &#123;</span><br><span class="line">    System.out.println(&quot;执行认证逻辑&quot;);</span><br><span class="line"></span><br><span class="line">    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;</span><br><span class="line"></span><br><span class="line">    User user = userService.getUserByUsername(token.getUsername());</span><br><span class="line"></span><br><span class="line">    if(user == null)&#123;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /*设置用户session*/</span><br><span class="line">    Session session = SecurityUtils.getSubject().getSession();</span><br><span class="line">    session.setAttribute(&quot;user&quot;,user);</span><br><span class="line">    System.out.println(session.getId());</span><br><span class="line"></span><br><span class="line">    return new SimpleAuthenticationInfo( (String)token.getPrincipal(), user.getPassword(),getName());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="授权"><a href="#授权" class="headerlink" title="授权"></a>授权</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) &#123;</span><br><span class="line">    System.out.println(&quot;执行授权逻辑&quot;);</span><br><span class="line">    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();</span><br><span class="line">    String username = (String) SecurityUtils.getSubject().getPrincipal();</span><br><span class="line"></span><br><span class="line">    Set&lt;String&gt; roles = new HashSet&lt;&gt;();</span><br><span class="line">    Set&lt;String&gt; permissions = new HashSet&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    User user = userService.getUserByUsername(username);</span><br><span class="line"></span><br><span class="line">    /*获取并添加该用户角色*/</span><br><span class="line">    List&lt;Role&gt; roleList = userService.getRoleByUserId(user.getId());</span><br><span class="line">    for(Role role: roleList)&#123;</span><br><span class="line">        roles.add(role.getRole());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /*获取并添加该用户权限*/</span><br><span class="line">    List&lt;Permission&gt; permissionList = userService.getPermissionByUserId(user.getId());</span><br><span class="line">    for(Permission permission: permissionList)&#123;</span><br><span class="line">        permissions.add(permission.getName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    authorizationInfo.addRoles(roles);</span><br><span class="line">    authorizationInfo.addStringPermissions(permissions);</span><br><span class="line"></span><br><span class="line">    return authorizationInfo;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里添加的角色是role.getRole()，权限是permission.getName()，如果使用的字段不同，对应的角色与权限名也不同，比如role为”vip白金会员”，则需要”vip白金会员”这个角色字符串，permission为”vip1”则需要permission为”vip1”。<br>至此，已经通过用户登录后通过session保持会话，那么如何设置角色或者权限拦截呢？</p>
<h4 id="有两种方法，一种是在控制层添加注解"><a href="#有两种方法，一种是在控制层添加注解" class="headerlink" title="有两种方法，一种是在控制层添加注解"></a>有两种方法，一种是在控制层添加注解</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">@RequiresPermissions(&quot;vip1&quot;)</span><br><span class="line">@RequestMapping(&quot;/vip1&quot;)</span><br><span class="line">public String vip1()&#123;</span><br><span class="line">    return &quot;This is vip1 content(尊贵vip1接口)&quot;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@RequiresPermissions(&quot;vip2&quot;)</span><br><span class="line">@RequestMapping(&quot;/vip2&quot;)</span><br><span class="line">public String vip2()&#123;</span><br><span class="line">    return &quot;This is vip2 content(尊贵vip2接口)&quot;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@RequiresRoles(&quot;vip黄金会员&quot;)</span><br><span class="line">@RequestMapping(&quot;/vip1_role&quot;)</span><br><span class="line">public String vip1_role()&#123;</span><br><span class="line">    return &quot;This is vip 黄金会员&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果要使用注解，必须要在ShiroConfig中添加下面的代码开启注解，否则无效</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">/*开启注解*/</span><br><span class="line">@Bean</span><br><span class="line">public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() &#123;</span><br><span class="line">    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();</span><br><span class="line">    advisorAutoProxyCreator.setProxyTargetClass(true);</span><br><span class="line">    return advisorAutoProxyCreator;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Bean</span><br><span class="line">public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() &#123;</span><br><span class="line">    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();</span><br><span class="line">    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());</span><br><span class="line">    return authorizationAttributeSourceAdvisor;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>访问/vip1接口就需要所需权限，未登录情况下<br><img src="http://106.15.200.82/source/1571804377475.jpg" alt="image.png"><br>登陆后<br><img src="http://106.15.200.82/source/1571804400820.jpg" alt="image.png"><br>访问<br><img src="http://106.15.200.82/source/1571804418056.jpg" alt="image.png"></p>
<p><img src="http://106.15.200.82/source/1571804434170.jpg" alt="image.png"><br>因为数据库中已经设定vip2也拥有vip1的所有权限，所以vip2用户可以访问vip1和vip2两个接口，但是如果登录vip1账户，只能访问vip1接口，vip2则访问不了。</p>
<h4 id="另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过"><a href="#另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过" class="headerlink" title="另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过"></a>另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">map.put(&quot;/perms&quot;,&quot;perms[vip2]&quot;);</span><br></pre></td></tr></table></figure>
<p>这样就需要vip2的权限才能访问”/perms”这个接口了，添加角色拦截也是一样，甚至数据库两个表也行，就user与role两个表也能达到安全管理的作用。</p>
<h5 id="设置没有权限返回的接口"><a href="#设置没有权限返回的接口" class="headerlink" title="设置没有权限返回的接口"></a>设置没有权限返回的接口</h5><p>添加一个抛出无权限异常的类</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">/*无权限异常抛出类*/</span><br><span class="line"></span><br><span class="line">@ControllerAdvice</span><br><span class="line">public class Exception &#123;</span><br><span class="line"></span><br><span class="line">    @ExceptionHandler</span><br><span class="line">    @ResponseBody</span><br><span class="line">    public String ErrotHandler(AuthorizationException e)&#123;</span><br><span class="line">        return &quot;Sorry,you don&apos;t have permission.&quot;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="Shiro-md5加密，加盐"><a href="#Shiro-md5加密，加盐" class="headerlink" title="Shiro md5加密，加盐"></a>Shiro md5加密，加盐</h4><p>安全起见，一般数据库中都不会保存明文的密码，通过md5这种非对称性算法，将密码加密后存储到数据库中，这种非对称性加密是单向的，通过转换后的密码无法解密成真实密码，所以用户如果忘记密码无法找回密码。但是只用md5加密有可能造成许多用户的密码重复，如一些简单的1234诸如此类的密码，所以再次通过数据库中唯一的id来加盐，达到密码唯一的效果。</p>
<h6 id="在ShiroConfig类中加入Bean"><a href="#在ShiroConfig类中加入Bean" class="headerlink" title="在ShiroConfig类中加入Bean"></a>在ShiroConfig类中加入Bean</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**加密方式*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> HashedCredentialsMatcher <span class="title">hashedCredentialsMatcher</span><span class="params">()</span></span>&#123;</span><br><span class="line">    HashedCredentialsMatcher hashedCredentialsMatcher = <span class="keyword">new</span> HashedCredentialsMatcher();</span><br><span class="line">    hashedCredentialsMatcher.setHashAlgorithmName(<span class="string">"md5"</span>);</span><br><span class="line">    hashedCredentialsMatcher.setHashIterations(<span class="number">3</span>);</span><br><span class="line">    <span class="comment">// 默认是true，true使用Hex编码；false使用Base64编码</span></span><br><span class="line">    hashedCredentialsMatcher.setStoredCredentialsHexEncoded(ture);</span><br><span class="line">    <span class="keyword">return</span> hashedCredentialsMatcher;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="修改ShiroConfig类中的Bean-Realm"><a href="#修改ShiroConfig类中的Bean-Realm" class="headerlink" title="修改ShiroConfig类中的Bean-Realm"></a>修改ShiroConfig类中的Bean-Realm</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> DefaultAdvisorAutoProxyCreator <span class="title">advisorAutoProxyCreator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = <span class="keyword">new</span> DefaultAdvisorAutoProxyCreator();</span><br><span class="line">    advisorAutoProxyCreator.setProxyTargetClass(<span class="keyword">true</span>);</span><br><span class="line">    <span class="keyword">return</span> advisorAutoProxyCreator;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样在执行认证逻辑的时候，Shiro会将用户的密码加密成配置的加密密文，与数据库中的密文进行对比，在此之前，还要根据配置的加密方式自己制定相应的加密方法。</p>
<h6 id="添加一个容器类Encryption"><a href="#添加一个容器类Encryption" class="headerlink" title="添加一个容器类Encryption"></a>添加一个容器类Encryption</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.apache.shiro.crypto.hash.SimpleHash;</span><br><span class="line"><span class="keyword">import</span> org.apache.shiro.util.ByteSource;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Encryption</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">md5Encryption</span><span class="params">(String username, String password)</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SimpleHash</span><br><span class="line">                (<span class="string">"MD5"</span>, password, ByteSource.Util.bytes</span><br><span class="line">                        (username), <span class="number">3</span>).toHex();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后用户注册的时候，通过注入方法来加密到数据库。</p>
<h6 id="最后修改下Realm的认证逻辑"><a href="#最后修改下Realm的认证逻辑" class="headerlink" title="最后修改下Realm的认证逻辑"></a>最后修改下Realm的认证逻辑</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**身份认证*/</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> AuthenticationInfo <span class="title">doGetAuthenticationInfo</span><span class="params">(AuthenticationToken authenticationToken)</span> <span class="keyword">throws</span> AuthenticationException </span>&#123;</span><br><span class="line">    System.out.println(<span class="string">"执行认证逻辑"</span>);</span><br><span class="line"></span><br><span class="line">    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;</span><br><span class="line"></span><br><span class="line">    User user = userService.getUserByUsername(token.getUsername());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(user == <span class="keyword">null</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">     <span class="keyword">return</span> <span class="keyword">new</span> SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getUsername()), getName());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>大功告成</p>
<h4 id="设置rememberMe状态保留"><a href="#设置rememberMe状态保留" class="headerlink" title="设置rememberMe状态保留"></a>设置rememberMe状态保留</h4><p>登陆成功后，关闭浏览器然后再次进入权限接口，会跳转到无权限的接口（页面），为了实现remember me的需求，让我们关闭浏览器后再次打开浏览器能够继续访问权限接口，需要在shiro中进行一些配置。</p>
<h6 id="添加两个Bean"><a href="#添加两个Bean" class="headerlink" title="添加两个Bean"></a>添加两个Bean</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**配置cookie，设置用户cookie失效时间，实现rememberMe功能。*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> SimpleCookie <span class="title">simpleCookie</span><span class="params">()</span></span>&#123;</span><br><span class="line">    SimpleCookie cookie = <span class="keyword">new</span> SimpleCookie(<span class="string">"rememberMe"</span>);</span><br><span class="line">    cookie.setMaxAge(<span class="number">500</span>);</span><br><span class="line">    <span class="keyword">return</span> cookie;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**管理对象*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> CookieRememberMeManager <span class="title">cookieRememberMeManager</span><span class="params">()</span></span>&#123;</span><br><span class="line">    CookieRememberMeManager rememberMeManager = <span class="keyword">new</span> CookieRememberMeManager();</span><br><span class="line">    rememberMeManager.setCookie(simpleCookie());</span><br><span class="line">    rememberMeManager.setCipherKey(Base64.decode(<span class="string">"4AvVhmFLUs0KTA3Kprsdag=="</span>));</span><br><span class="line">    <span class="keyword">return</span> rememberMeManager;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="然后添加到SecurityManager中注入上面两个Bean"><a href="#然后添加到SecurityManager中注入上面两个Bean" class="headerlink" title="然后添加到SecurityManager中注入上面两个Bean"></a>然后添加到SecurityManager中注入上面两个Bean</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span>(name = <span class="string">"securityManager"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> DefaultWebSecurityManager <span class="title">securityManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    DefaultWebSecurityManager defaultWebSecurityManager =</span><br><span class="line">            <span class="keyword">new</span> DefaultWebSecurityManager();</span><br><span class="line">    defaultWebSecurityManager.setRealm(realm());</span><br><span class="line">    defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());</span><br><span class="line">    <span class="keyword">return</span> defaultWebSecurityManager;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="在ShiroFilterFactoryBean中设置接口（页面）"><a href="#在ShiroFilterFactoryBean中设置接口（页面）" class="headerlink" title="在ShiroFilterFactoryBean中设置接口（页面）"></a>在ShiroFilterFactoryBean中设置接口（页面）</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">@Bean(name = &quot;shiroFilter&quot;)</span><br><span class="line">public ShiroFilterFactoryBean shiroFilter()&#123;</span><br><span class="line">    ShiroFilterFactoryBean shiroFilterFactoryBean =</span><br><span class="line">            new ShiroFilterFactoryBean();</span><br><span class="line">    shiroFilterFactoryBean.setSecurityManager(securityManager());</span><br><span class="line">    shiroFilterFactoryBean.setLoginUrl(&quot;/none&quot;);</span><br><span class="line">    shiroFilterFactoryBean.setUnauthorizedUrl(&quot;/none&quot;);</span><br><span class="line">    Map&lt;String,String&gt; map = new LinkedHashMap&lt;String,String&gt;();</span><br><span class="line">    map.put(&quot;/list&quot;,&quot;anon&quot;);</span><br><span class="line">    map.put(&quot;/login&quot;,&quot;anon&quot;);</span><br><span class="line">    map.put(&quot;/test&quot;,&quot;anon&quot;);</span><br><span class="line">    map.put(&quot;/info&quot;,&quot;user&quot;);</span><br><span class="line">    // 可以使用过滤器拦截未授权url，也可以使用注解*/</span><br><span class="line">    map.put(&quot;/perms&quot;,&quot;perms[vip2]&quot;);</span><br><span class="line">    map.put(&quot;/*&quot;,&quot;authc&quot;);</span><br><span class="line">    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);</span><br><span class="line"></span><br><span class="line">    return shiroFilterFactoryBean;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到 “/info” 接口的value值为 “user”，这样 “/info” 接口在浏览器退出后，再次进入浏览器可以无需登录访问。</p>
<h6 id="最后在-login-接口中"><a href="#最后在-login-接口中" class="headerlink" title="最后在 /login 接口中"></a>最后在 /login 接口中</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">@RequestMapping(&quot;/login&quot;)</span><br><span class="line">public String login(@RequestParam(&quot;username&quot;) String username, @RequestParam(&quot;password&quot;)String password, @RequestParam(value = &quot;remember&quot;, required = true, defaultValue = &quot;false&quot;)boolean remember)&#123;</span><br><span class="line">    /**创建一个subject*/</span><br><span class="line">    Subject subject = SecurityUtils.getSubject();</span><br><span class="line">    /**封装用户数据*/</span><br><span class="line">    UsernamePasswordToken token = new UsernamePasswordToken(username, password, remember);</span><br><span class="line"></span><br><span class="line">    /**执行登录,然后这里会跳转到Realm的认证逻辑中*/</span><br><span class="line">    subject.login(token);</span><br><span class="line">    token.setRememberMe(remember);</span><br><span class="line">    HttpSession session = request.getSession();</span><br><span class="line">    session.setAttribute(&quot;user&quot;, subject.getPrincipal());</span><br><span class="line">    return session.getId();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>remember默认为false</p>
<h6 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h6><p>首先设置remember为false</p>
<p><img src="http://106.15.200.82/source/1573195336847.jpg" alt="image.png"></p>
<p><img src="http://106.15.200.82/source/1573195656698.jpg" alt="image.png"></p>
<p>登陆成功且接口正常访问。然后退出浏览器。再次访问接口。</p>
<p><img src="http://106.15.200.82/source/1573195510558.jpg" alt="image.png"></p>
<p>可以发现无法访问。接下来设置rememberMe为true</p>
<p><img src="http://106.15.200.82/source/1573195603643.jpg" alt="image.png"></p>
<p>退出浏览器后，再次访问。</p>
<p><img src="http://106.15.200.82/source/1573613696767.jpg" alt="1573195740184"></p>
<p>测试成功！</p>
<h4 id="Shiro整合session-redis实现分布式共享session"><a href="#Shiro整合session-redis实现分布式共享session" class="headerlink" title="Shiro整合session+redis实现分布式共享session"></a>Shiro整合session+redis实现分布式共享session</h4><p>随着用户量的增多，内存中的session也会随之大量增长，严重影响java性能，此时，存在一个方案，将session放在redis缓存中进行管理，将配置好的redis和session放入shiro的securityManager中。</p>
<h6 id="第一步添加依赖"><a href="#第一步添加依赖" class="headerlink" title="第一步添加依赖"></a>第一步添加依赖</h6><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-spring<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.3.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.crazycake<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.4.2.1-RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>shiro-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.shiro<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h6 id="shiroConfig配置中"><a href="#shiroConfig配置中" class="headerlink" title="shiroConfig配置中"></a>shiroConfig配置中</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**配置redis缓存管理器*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> RedisManager <span class="title">redisManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    RedisManager manager = <span class="keyword">new</span> RedisManager();</span><br><span class="line">    manager.setHost(<span class="string">"127.0.0.1"</span>);</span><br><span class="line">    manager.setPort(<span class="number">6379</span>);</span><br><span class="line">    manager.setExpire(<span class="number">1800</span>);</span><br><span class="line">    <span class="keyword">return</span> manager;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**配置RedisSessionDao*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> RedisSessionDAO <span class="title">redisSessionDAO</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    RedisSessionDAO sessionDAO = <span class="keyword">new</span> RedisSessionDAO();</span><br><span class="line">    sessionDAO.setRedisManager(redisManager());</span><br><span class="line">    <span class="keyword">return</span> sessionDAO;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**Session管理器*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> DefaultWebSessionManager <span class="title">defaultWebSessionManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    DefaultWebSessionManager manager = <span class="keyword">new</span> DefaultWebSessionManager();</span><br><span class="line">    manager.setSessionDAO(redisSessionDAO());</span><br><span class="line">    <span class="keyword">return</span> manager;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**shiro缓存管理*/</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> RedisCacheManager <span class="title">redisCacheManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    RedisCacheManager manager = <span class="keyword">new</span> RedisCacheManager();</span><br><span class="line">    manager.setRedisManager(redisManager());</span><br><span class="line">    <span class="keyword">return</span> manager;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="添加到securityManager中"><a href="#添加到securityManager中" class="headerlink" title="添加到securityManager中"></a>添加到securityManager中</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span>(name = <span class="string">"securityManager"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> DefaultWebSecurityManager <span class="title">securityManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    DefaultWebSecurityManager defaultWebSecurityManager =</span><br><span class="line">            <span class="keyword">new</span> DefaultWebSecurityManager();</span><br><span class="line">    defaultWebSecurityManager.setRealm(realm());</span><br><span class="line">    defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());</span><br><span class="line">    defaultWebSecurityManager.setSessionManager(defaultWebSessionManager());</span><br><span class="line">    defaultWebSecurityManager.setCacheManager(redisCacheManager());</span><br><span class="line">    <span class="keyword">return</span> defaultWebSecurityManager;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="测试-1"><a href="#测试-1" class="headerlink" title="测试"></a>测试</h6><p>登录</p>
<p><img src="http://106.15.200.82/source/1573619171317.jpg" alt=""></p>
<p>查看缓存</p>
<p><img src="http://106.15.200.82/source/1573619237325.jpg" alt=""></p>
<p>测试成功！</p>
<h4 id="Shiro登陆失败次数限制"><a href="#Shiro登陆失败次数限制" class="headerlink" title="Shiro登陆失败次数限制"></a>Shiro登陆失败次数限制</h4><p>为了防止暴力破解密码，需要对登录次数进行限制，失败次数达到一定次数后，规定时间内限制其再次登陆。首先用户表需要添加status字段。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">public class User implements Serializable &#123;</span><br><span class="line"></span><br><span class="line">    private Integer id;</span><br><span class="line">    private String username;</span><br><span class="line">    private String password;</span><br><span class="line">	private Integer status;</span><br><span class="line">    /*get and set*/</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后在Realm认证逻辑中，1表示已经被锁定，0表示正常。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">/**身份认证*/</span><br><span class="line">@Override</span><br><span class="line">protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException &#123;</span><br><span class="line">    System.out.println(&quot;执行认证逻辑&quot;);</span><br><span class="line"></span><br><span class="line">    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;</span><br><span class="line"></span><br><span class="line">    User user = userService.getUserByUsername(token.getUsername());</span><br><span class="line"></span><br><span class="line">    if(user == null)&#123;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if(user.getStatus == 1)&#123;</span><br><span class="line">        throw new LockedAccountException(&quot;Account Locked.&quot;);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getUsername()), getName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后配置搞缓存和在错误登陆的时候+1 balabalabala</p>
<h3 id="数据库表设计"><a href="#数据库表设计" class="headerlink" title="数据库表设计"></a>数据库表设计</h3><h4 id="用户表"><a href="#用户表" class="headerlink" title="用户表"></a>用户表</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `user` (</span><br><span class="line">  `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  `user_id` varchar(20) NOT NULL COMMENT &apos;用户id&apos;,</span><br><span class="line">  `username` varchar(50) NOT NULL COMMENT &apos;用户名&apos;,</span><br><span class="line">  `password` varchar(50) NOT NULL,</span><br><span class="line">  `salt` varchar(128) DEFAULT NULL COMMENT &apos;加密盐值&apos;,</span><br><span class="line">  `email` varchar(50) DEFAULT NULL COMMENT &apos;邮箱&apos;,</span><br><span class="line">  `phone` varchar(50) DEFAULT NULL COMMENT &apos;联系方式&apos;,</span><br><span class="line">  `sex` int(255) DEFAULT NULL COMMENT &apos;年龄：1男2女&apos;,</span><br><span class="line">  `age` int(3) DEFAULT NULL COMMENT &apos;年龄&apos;,</span><br><span class="line">  `status` int(1) NOT NULL COMMENT &apos;用户状态：1有效; 2删除&apos;,</span><br><span class="line">  `create_time` datetime DEFAULT NULL COMMENT &apos;创建时间&apos;,</span><br><span class="line">  `update_time` datetime DEFAULT NULL COMMENT &apos;更新时间&apos;,</span><br><span class="line">  `last_login_time` datetime DEFAULT NULL COMMENT &apos;最后登录时间&apos;,</span><br><span class="line">  PRIMARY KEY (`id`,`user_id`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;</span><br></pre></td></tr></table></figure>
<h4 id="角色表"><a href="#角色表" class="headerlink" title="角色表"></a>角色表</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `role` (</span><br><span class="line">  `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  `role_id` varchar(20) NOT NULL COMMENT &apos;角色id&apos;,</span><br><span class="line">  `name` varchar(50) NOT NULL COMMENT &apos;角色名称&apos;,</span><br><span class="line">  `description` varchar(255) DEFAULT NULL COMMENT &apos;角色描述&apos;,</span><br><span class="line">  `status` int(1) NOT NULL COMMENT &apos;状态：1有效；2删除&apos;,</span><br><span class="line">  `create_time` datetime DEFAULT NULL COMMENT &apos;创建时间&apos;,</span><br><span class="line">  `update_time` datetime DEFAULT NULL COMMENT &apos;更新时间&apos;,</span><br><span class="line">  PRIMARY KEY (`id`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;</span><br></pre></td></tr></table></figure>
<h4 id="权限表"><a href="#权限表" class="headerlink" title="权限表"></a>权限表</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `permission` (</span><br><span class="line">  `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  `permission_id` varchar(20) NOT NULL COMMENT &apos;权限id&apos;,</span><br><span class="line">  `name` varchar(100) NOT NULL COMMENT &apos;权限名称&apos;,</span><br><span class="line">  `description` varchar(255) DEFAULT NULL COMMENT &apos;权限描述&apos;,</span><br><span class="line">  `url` varchar(255) DEFAULT NULL COMMENT &apos;权限访问路径&apos;,</span><br><span class="line">  `perms` varchar(255) DEFAULT NULL COMMENT &apos;权限标识&apos;,</span><br><span class="line">  `parent_id` int(11) DEFAULT NULL COMMENT &apos;父级权限id&apos;,</span><br><span class="line">  `type` int(1) DEFAULT NULL COMMENT &apos;类型   0：目录   1：菜单   2：按钮&apos;,</span><br><span class="line">  `order_num` int(3) DEFAULT &apos;0&apos; COMMENT &apos;排序&apos;,</span><br><span class="line">  `icon` varchar(50) DEFAULT NULL COMMENT &apos;图标&apos;,</span><br><span class="line">  `status` int(1) NOT NULL COMMENT &apos;状态：1有效；2删除&apos;,</span><br><span class="line">  `create_time` datetime DEFAULT NULL,</span><br><span class="line">  `update_time` datetime DEFAULT NULL,</span><br><span class="line">  PRIMARY KEY (`id`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;</span><br></pre></td></tr></table></figure>
<h4 id="用户角色关系表"><a href="#用户角色关系表" class="headerlink" title="用户角色关系表"></a>用户角色关系表</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `user_role` (</span><br><span class="line">  `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  `user_id` varchar(20) NOT NULL COMMENT &apos;用户id&apos;,</span><br><span class="line">  `role_id` varchar(20) NOT NULL COMMENT &apos;角色id&apos;,</span><br><span class="line">  PRIMARY KEY (`id`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;</span><br></pre></td></tr></table></figure>
<h4 id="角色权限关系表"><a href="#角色权限关系表" class="headerlink" title="角色权限关系表"></a>角色权限关系表</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `role_permission` (</span><br><span class="line">  `id` int(11) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  `role_id` varchar(20) NOT NULL COMMENT &apos;角色id&apos;,</span><br><span class="line">  `permission_id` varchar(20) NOT NULL COMMENT &apos;权限id&apos;,</span><br><span class="line">  PRIMARY KEY (`id`)</span><br><span class="line">) ENGINE=InnoDB AUTO_INCREMENT=869 DEFAULT CHARSET=utf8;</span><br></pre></td></tr></table></figure>
<h4 id="session会话管理"><a href="#session会话管理" class="headerlink" title="session会话管理"></a>session会话管理</h4><p><img src="http://106.15.200.82/source/1571762848189.jpg" alt="image.png"></p>

  </div>
</article>



        
          <div id="footer-post-container">
  <div id="footer-post">

    <div id="nav-footer" style="display: none">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Writing</a></li>
         
          <li><a href="/projects_url">Projects</a></li>
        
      </ul>
    </div>

    <div id="toc-footer" style="display: none">
      <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#接下来我们开始简单的整合"><span class="toc-number">1.</span> <span class="toc-text">接下来我们开始简单的整合</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#pom-xml添加"><span class="toc-number">1.1.</span> <span class="toc-text">pom.xml添加</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#第一步就是手写一个配置类，命名为ShiroConfig"><span class="toc-number">1.2.</span> <span class="toc-text">第一步就是手写一个配置类，命名为ShiroConfig</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接下来手写Realm类"><span class="toc-number">1.3.</span> <span class="toc-text">接下来手写Realm类</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Controller，在Controller中写刚刚的几个url"><span class="toc-number">1.4.</span> <span class="toc-text">Controller，在Controller中写刚刚的几个url</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接下来使用postman进行接口测试"><span class="toc-number">1.5.</span> <span class="toc-text">接下来使用postman进行接口测试</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#输入不需要认证的url-“-list”进行测试"><span class="toc-number">1.5.1.</span> <span class="toc-text">输入不需要认证的url “/list”进行测试</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#输入需要认证的url进行测试"><span class="toc-number">1.5.2.</span> <span class="toc-text">输入需要认证的url进行测试</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#现在开始认证"><span class="toc-number">1.5.3.</span> <span class="toc-text">现在开始认证</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#再次输入需要认证的url-“-info”"><span class="toc-number">1.5.4.</span> <span class="toc-text">再次输入需要认证的url “/info”</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#角色和权限的关系"><span class="toc-number">1.6.</span> <span class="toc-text">角色和权限的关系</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#首先是数据库设计，先给出一个简单的数据表"><span class="toc-number">1.7.</span> <span class="toc-text">首先是数据库设计，先给出一个简单的数据表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）"><span class="toc-number">1.8.</span> <span class="toc-text">接下来要写几个接口，一个是通过用户id获取该用户所拥有的角色，一个是通过用户id获取该用户角色拥有的权限，一个是通过用户名查找id（毕竟用户输入账户不会输入id）</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#DAO数据层使用简单的-Mapper注解"><span class="toc-number">1.8.1.</span> <span class="toc-text">DAO数据层使用简单的@Mapper注解</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#业务层接口"><span class="toc-number">1.8.2.</span> <span class="toc-text">业务层接口</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#注入service业务层接口实现"><span class="toc-number">1.8.3.</span> <span class="toc-text">注入service业务层接口实现</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#现在认证可以使用数据库了"><span class="toc-number">1.8.4.</span> <span class="toc-text">现在认证可以使用数据库了</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#授权"><span class="toc-number">1.8.5.</span> <span class="toc-text">授权</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#有两种方法，一种是在控制层添加注解"><span class="toc-number">1.9.</span> <span class="toc-text">有两种方法，一种是在控制层添加注解</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过"><span class="toc-number">1.10.</span> <span class="toc-text">另一种方法，在ShiroConfig中添加拦截页面，拦截页面已经在上面说过</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#设置没有权限返回的接口"><span class="toc-number">1.10.1.</span> <span class="toc-text">设置没有权限返回的接口</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Shiro-md5加密，加盐"><span class="toc-number">1.11.</span> <span class="toc-text">Shiro md5加密，加盐</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#在ShiroConfig类中加入Bean"><span class="toc-number">1.11.0.1.</span> <span class="toc-text">在ShiroConfig类中加入Bean</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#修改ShiroConfig类中的Bean-Realm"><span class="toc-number">1.11.0.2.</span> <span class="toc-text">修改ShiroConfig类中的Bean-Realm</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#添加一个容器类Encryption"><span class="toc-number">1.11.0.3.</span> <span class="toc-text">添加一个容器类Encryption</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#最后修改下Realm的认证逻辑"><span class="toc-number">1.11.0.4.</span> <span class="toc-text">最后修改下Realm的认证逻辑</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#设置rememberMe状态保留"><span class="toc-number">1.12.</span> <span class="toc-text">设置rememberMe状态保留</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#添加两个Bean"><span class="toc-number">1.12.0.1.</span> <span class="toc-text">添加两个Bean</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#然后添加到SecurityManager中注入上面两个Bean"><span class="toc-number">1.12.0.2.</span> <span class="toc-text">然后添加到SecurityManager中注入上面两个Bean</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#在ShiroFilterFactoryBean中设置接口（页面）"><span class="toc-number">1.12.0.3.</span> <span class="toc-text">在ShiroFilterFactoryBean中设置接口（页面）</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#最后在-login-接口中"><span class="toc-number">1.12.0.4.</span> <span class="toc-text">最后在 /login 接口中</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#测试"><span class="toc-number">1.12.0.5.</span> <span class="toc-text">测试</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Shiro整合session-redis实现分布式共享session"><span class="toc-number">1.13.</span> <span class="toc-text">Shiro整合session+redis实现分布式共享session</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#第一步添加依赖"><span class="toc-number">1.13.0.1.</span> <span class="toc-text">第一步添加依赖</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#shiroConfig配置中"><span class="toc-number">1.13.0.2.</span> <span class="toc-text">shiroConfig配置中</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#添加到securityManager中"><span class="toc-number">1.13.0.3.</span> <span class="toc-text">添加到securityManager中</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#测试-1"><span class="toc-number">1.13.0.4.</span> <span class="toc-text">测试</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Shiro登陆失败次数限制"><span class="toc-number">1.14.</span> <span class="toc-text">Shiro登陆失败次数限制</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#数据库表设计"><span class="toc-number">2.</span> <span class="toc-text">数据库表设计</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#用户表"><span class="toc-number">2.1.</span> <span class="toc-text">用户表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#角色表"><span class="toc-number">2.2.</span> <span class="toc-text">角色表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#权限表"><span class="toc-number">2.3.</span> <span class="toc-text">权限表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#用户角色关系表"><span class="toc-number">2.4.</span> <span class="toc-text">用户角色关系表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#角色权限关系表"><span class="toc-number">2.5.</span> <span class="toc-text">角色权限关系表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#session会话管理"><span class="toc-number">2.6.</span> <span class="toc-text">session会话管理</span></a></li></ol></li></ol>
    </div>

    <div id="share-footer" style="display: none">
      <ul>
  <li><a class="icon" href="http://www.facebook.com/sharer.php?u=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/" target="_blank" rel="noopener"><i class="fab fa-facebook fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://twitter.com/share?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&text=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-twitter fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.linkedin.com/shareArticle?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-linkedin fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&is_video=false&description=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-pinterest fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=安全权限框架之-springboot整合shiro&body=Check out this article: http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/" target="_blank" rel="noopener"><i class="fas fa-envelope fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://getpocket.com/save?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://reddit.com/submit?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-reddit fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.stumbleupon.com/submit?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-stumbleupon fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://digg.com/submit?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&title=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-digg fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.tumblr.com/share/link?url=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&name=安全权限框架之-springboot整合shiro&description=" target="_blank" rel="noopener"><i class="fab fa-tumblr fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://news.ycombinator.com/submitlink?u=http://yoursite.com/2019/10/31/%E5%AE%89%E5%85%A8%E6%9D%83%E9%99%90%E6%A1%86%E6%9E%B6%E4%B9%8B-springboot%E6%95%B4%E5%90%88shiro/&t=安全权限框架之-springboot整合shiro" target="_blank" rel="noopener"><i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i></a></li>
</ul>

    </div>

    <div id="actions-footer">
        <a id="menu" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;"><i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
        <a id="toc" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;"><i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
        <a id="share" class="icon" href="#" onclick="$('#share-footer').toggle();return false;"><i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> Share</a>
        <a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
    </div>

  </div>
</div>

        
        <footer id="footer">
  <div class="footer-left">
    Copyright &copy; 2020 ECIN520
  </div>
  <div class="footer-right">
    <nav>
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Writing</a></li>
         
          <li><a href="/projects_url">Projects</a></li>
        
      </ul>
    </nav>
  </div>
</footer>

    </div>
    <!-- styles -->
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<link rel="stylesheet" href="/lib/justified-gallery/css/justifiedGallery.min.css">

    <!-- jquery -->
<script src="/lib/jquery/jquery.min.js"></script>
<script src="/lib/justified-gallery/js/jquery.justifiedGallery.min.js"></script>
<!-- clipboard -->

  <script src="/lib/clipboard/clipboard.min.js"></script>
  <script type="text/javascript">
  $(function() {
    // copy-btn HTML
    var btn = "<span class=\"btn-copy tooltipped tooltipped-sw\" aria-label=\"Copy to clipboard!\">";
    btn += '<i class="far fa-clone"></i>';
    btn += '</span>'; 
    // mount it!
    $(".highlight table").before(btn);
    var clip = new ClipboardJS('.btn-copy', {
      text: function(trigger) {
        return Array.from(trigger.nextElementSibling.querySelectorAll('.code')).reduce((str,it)=>str+it.innerText+'\n','')
      }
    });
    clip.on('success', function(e) {
      e.trigger.setAttribute('aria-label', "Copied!");
      e.clearSelection();
    })
  })
  </script>

<script src="/js/main.js"></script>
<!-- search -->

<!-- Google Analytics -->

<!-- Baidu Analytics -->

<!-- Disqus Comments -->


</body>
</html>
